<template>
    <div>
        <div :id="cesiumDomId" class="cesium-container">
            <transition name="opacity-change" appear>
                <div class="location-hint-container" v-show="locationHint.show">
                    <span class="location-hint-container__title">{{ locationHint.title }}</span>
                </div>
            </transition>
            <static-map 
                class="static-map"
                :modelName="staticMapData.modelName"
                :zoomLevel="staticMapData.zoomLevel"
                ref="staticMap"
            />
            <transition name="opacity-change" appear>
                <div class="function-area-container" v-show="btnShowState">
                    <el-button 
                        class="aside-function"
                        icon="el-icon-s-fold"
                        size="medium" 
                        circle
                        @click="handleAsideButtonClick($event)">
                    </el-button>
                    <el-button
                        class="back-function"
                        icon="el-icon-s-release"
                        size="medium" 
                        circle
                        @click="handleBackButtonClick($event)">
                    </el-button>
                    <el-button
                        class="back-to-entrance-function"
                        icon="el-icon-s-home"
                        size="medium"
                        circle
                        @click="handleBack2EntranceClick($event)">
                    </el-button>
                    <el-button
                        class="show-info-dialog-function"
                        icon="el-icon-info"
                        size="medium" 
                        circle
                        @click="handleInfoButtonClick($event)">
                    </el-button>
                    <el-button
                        v-if="isFeatureFocus"
                        class="show-click-rotate"
                        icon="el-icon-video-camera-solid"
                        size="medium"
                        circle
                        @click="handleClickRotate($event)">
                    </el-button>
                </div>
            </transition>
            <feature-focus class="feature-focus-container" v-if="showClickRotate"/>
        </div>
        <location-list-aside 
            ref="aside"
            :locationList="asideLocationList"
            :visualLevel="asideVisualLevel"
            :modelIndex="modelStorage.modelId"
        />
        <info-display-dialog ref="mainDialog" dialogType="building"/>
        <info-display-dialog ref="subDialog" dialogType="item" v-if="visualLevel > 2"/>
        <inner-camera v-if="changeCameraMode" :entranceCameraHpr="modelStorage.hpr" :floorHeight="preInnerFloorHeight"/>
        <scene-picker v-if="isInnerRoom" :innerRoomId="modelStorage.modelId"/>
    </div>
</template>

<script>
import 'widgets.css';
import SceneViewer from '@/cesium/scene-viewer';
import { MyCesiumConsts, MyAmapConsts, StaticHints } from '@/constant';
import TilesetHandler from '@/cesium/tileset-handler';
import { Cartesian3, ImageryLayer, UrlTemplateImageryProvider, PrimitiveCollection, ScreenSpaceEventHandler, Cartographic } from 'cesium';
import { IonWorldImageryStyle, ScreenSpaceEventType, PostProcessStageLibrary, Math } from 'cesium';
import StaticMap from '@/components/StaticMap';
import LocationListAside from '@/components/LocationListAside';
import FeatureFocus from '@/components/FeatureFocus';
import InfoDisplayDialog from '@/components/InfoDisplayDialog';
import InnerCamera from '@/components/InnerCamera';
import ScenePicker from '@/components/ScenePicker';
import { getModelAsideData, getFeatures2Replace, getFloorAndRoomAsideData, getFloorModel, getInnerRoomModel } from '@/request/request-api';
import { getInteractiveItemAside, getHighlightItemBatchIds, getRestoreFeature, getSingleModelAsAside, getFloorWithEntrance } from '@/request/request-api';
import ClippingBoxManager from '@/cesium/clipping-box-manager';
import CameraController from '@/cesium/camera-controller';

export default {
    name: 'CesiumView',
    components: { StaticMap, LocationListAside, FeatureFocus, InfoDisplayDialog, InnerCamera, ScenePicker },
    data() {
        return {
            cesiumDomId: '',
            // control default canvas event handler
            userInputState: true,
            // which level be in now
            visualLevel: -1,
            asideVisualLevel: -1,
            locationHint: {
                title: '',
                show: false
            },
            staticMapData: {
                modelName: '',
                zoomLevel: 12,
            },
            btnShowState: false,
            asideLocationList: [],
            modelStorage: {
                modelId: '',
                index: -1,
                destination: {},
                hpr: {},
            },
            // object with modelId and visual level
            modelStack: [],
            isShowClickRotate: false,
            preInnerFloorHeight: 3,
        }
    },
    created() {
        var that = this;
        that.cesiumDomId = MyCesiumConsts.DEFAULT_CONTAINER_DOM_ID;
        // set initial visual level in outer space
        that.visualLevel = MyCesiumConsts.VISUAL_LEVEL.OUTER_SPACE;
    },
    mounted() {
        var that = this;
        that.initCesium();
        // that.loadCesium();
    },
    watch: {
        userInputState: {
            handler(newState) {
                CameraController.setCameraInputStatus(newState);
                this.btnShowState = newState;
            },
        }
    },
    computed: {
        isFeatureFocus() {
            return this.visualLevel === MyCesiumConsts.VISUAL_LEVEL.FEATURE_FOCUS;
        },
        showClickRotate() {
            return this.isFeatureFocus && this.isShowClickRotate;
        },
        changeCameraMode() {
            return this.visualLevel > MyCesiumConsts.VISUAL_LEVEL.FEATURE_FOCUS;
        },
        isInnerRoom() {
            return this.visualLevel === MyCesiumConsts.VISUAL_LEVEL.INNER_ROOM;
        },
    },
    methods: {
        async loadCesium() {
            var that = this;
            // get the global only Cesium.Viewer
            const sceneViewer = SceneViewer.getInstance(that.cesiumDomId);
            const viewer = sceneViewer.viewerInstance;
            console.log(sceneViewer);
            // set extra config
            that.setExtraConfigWhenInit(viewer);
            const result = await that.loadGlobeSurface(viewer);
            // if (result) {    
                            const bPromise = TilesetHandler.loadTilesetWithInitClippingBox(MyCesiumConsts.MODEL_PATH.OLD_GOVERNMENT_OUTER_BOX, viewer.scene, {
                translation: [-34.0, -64.0, 0.0]
            });
            bPromise.then(tileset => {
                const innerPromise = TilesetHandler.loadTilesetWithInitClippingBox(MyCesiumConsts.MODEL_PATH.OLD_GOVERNMENT_INNER_FLOOR, viewer.scene, {
                            translation: [-34.0, -64.0, 0.0],
                            isAccessible: true
                        });
                        
                        innerPromise.then(innerTileset => {
                            that.changeCameraLocation({
                                heading: 3.7210381323888075,
                                pitch: -0.10654360549667197,
                                roll: 6.283184997943442
                            }, false, () => {
                                ClippingBoxManager.adjustClippingBoxAroundCamera(innerTileset, new Cartesian3(-2850971.8466690434, 4654771.1415410284, 3288299.1677528662), 10);
                            }, -2850969.8427792946, 4654768.8957605278, 3288296.1424728339);
                        });  
            });      
                // const aPromise = TilesetHandler.loadTilesetWithInitClippingBox(MyCesiumConsts.MODEL_PATH.BUND, viewer.scene, {
                //     translation: [-1484.0, -1338.0, 0.0],
                //     is: true
                // });
                // aPromise.then(parentTileset => {
                //     that.userInputState = true;
                //     // viewer.flyTo(parentTileset);
                //     const bPromise = TilesetHandler.loadTilesetWithInitClippingBox(MyCesiumConsts.MODEL_PATH.OLD_GOVERNMENT_OUTER_BOX, viewer.scene, {
                //         translation: [-34.0, -64.0, 0.0]
                //     });
                //     bPromise.then(childTileset => {
                //         // ClippingBoxManager.adjustClippingBox2FeatureFocus(parentTileset, childTileset);
                //         // ClippingBoxManager.debugShowClippingBox(parentTileset);
                //         viewer.flyTo(childTileset);
                //     });
                // });
            //     viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) {
            //         const pickedFeature = viewer.scene.pick(movement.position);
            //         console.log(pickedFeature);
            //         // pickedFeature.show = false;
            //     }, ScreenSpaceEventType.LEFT_CLICK);
            //     const promise = TilesetHandler.loadTileset(MyCesiumConsts.MODEL_PATH.INDOOR_OUTER_BOX, viewer.scene, false);
            //     promise.then(tileset => {
            //         const innerPromise = TilesetHandler.loadTilesetWithInitClippingBox(MyCesiumConsts.MODEL_PATH.INDOOR_INNER_DECORATION, viewer.scene, {
            //             translation: [0, 0, 0],
            //             isAccessible: true,
            //             showClippingBox: true
            //         });
            //         innerPromise.then(innerTileset => {
            //             console.log(innerTileset);
            //             viewer.camera.flyTo({
            //                 destination: new Cartesian3(-2850958.56651834, 4654770.98848182, 3288300.12696263),
            //                 orientation: {
            //                     heading: 1.54633515876828,
            //                     pitch: 0.0469477822183069,
            //                     roll: 6.28318530174125
            //                 },
            //                 // complete: () => {
            //                 //     ClippingBoxManager.adjustClippingBoxAroundCamera(innerTileset, new Cartesian3(-2850958.56651834, 4654770.98848182, 3288300.12696263));
            //                 // }
            //             })
                        
            //         });
            //     });
            // }
        },
        async initCesium() {
            var that = this;
            // get the global only Cesium.Viewer
            const sceneViewer = SceneViewer.getInstance(that.cesiumDomId);
            const viewer = sceneViewer.viewerInstance;
            console.log(sceneViewer);
            // set extra config
            that.setExtraConfigWhenInit(viewer);
            // load imageries and wait to adjust camera
            const imageryLoadResult = await that.loadGlobeSurface(viewer);
            if (imageryLoadResult) {
                getModelAsideData()
                    .then(res => {
                        if (res && res.data && res.data.length !== 0) {
                            this.asideLocationList = res.data;
                            this.renderLocation(res.data[0], MyCesiumConsts.VISUAL_LEVEL.GLOBE_SURFACE);
                        }
                    });
            }
        },
        renderLocation(modelObj, visualLevelOfModelToRender) {
            var that = this;
            // don't need to render new location or model
            if (modelObj.myId === that.modelStorage.modelId) {
                return
            }
            // manually close user input when camera moves
            that.userInputState = false;

            var staticMapLoadFlag = false;
            var accessibleFlag = false;
            that.locationHint.title = modelObj.modelName;
            const lastVisualLevel = that.visualLevel;
            const viewer = window.viewer.viewerInstance;
            const _VISUAL_LEVEL = MyCesiumConsts.VISUAL_LEVEL;

            if (visualLevelOfModelToRender < _VISUAL_LEVEL.INNER_FLOOR) {
                staticMapLoadFlag = true;
                that.updateMiniMap(modelObj.entranceInfo, visualLevelOfModelToRender);
                if (visualLevelOfModelToRender === _VISUAL_LEVEL.GLOBE_SURFACE) {
                    // clear stack and camera
                    that.modelStack.length = 0;
                    viewer.camera.flyHome(3);
                    viewer.scene.primitives.removeAll();
                }
            }

            if (lastVisualLevel === _VISUAL_LEVEL.GLOBE_SURFACE && visualLevelOfModelToRender === _VISUAL_LEVEL.FEATURE_FOCUS) {
                // from GLOBE_SURFACE to FEATURE_FOCUS, may need to replace batchids
                getFeatures2Replace({
                    containerId: that.modelStorage.modelId,
                    substitutorId: modelObj.myId
                }).then(res => {
                    if (res.data) {
                        const replaceFeatures = res.data;
                        const parentTileset = viewer.scene.primitives.get(that.modelStorage.index);
                        replaceFeatures.forEach(replaceFeature => {
                            TilesetHandler.replaceFeaturesAfterLoad(
                                parentTileset,
                                replaceFeature.tileName,
                                replaceFeature.batchIds
                            );
                        });
                        that.renderModel(modelObj, staticMapLoadFlag, visualLevelOfModelToRender, accessibleFlag);
                    }
                });
            } else if (lastVisualLevel === _VISUAL_LEVEL.FEATURE_FOCUS && visualLevelOfModelToRender === _VISUAL_LEVEL.INNER_FLOOR) {
                // the first time from FEATURE_FOCUS to INNER_FLOOR, render the floor model
                getFloorModel({
                    parentId: that.modelStorage.modelId
                }).then(res => {
                    //TODO: averageFloorHeight
                    accessibleFlag = true;
                    if (res.data) {
                        const floorModel = res.data;
                        // composite a new modelObj
                        var _modelObj = {...modelObj};
                        _modelObj.myId = floorModel.myId;
                        _modelObj.modelPath = floorModel.modelPath;
                        _modelObj.translationArr = floorModel.translationArr;
                        _modelObj.averageFloorHeight = floorModel.averageFloorHeight;
                        that.preInnerFloorHeight = floorModel.averageFloorHeight;
                        that.renderModel(_modelObj, staticMapLoadFlag, visualLevelOfModelToRender, accessibleFlag);
                    }
                });
            } else if (lastVisualLevel === _VISUAL_LEVEL.OUTER_SPACE) {
                // the camera will enter from the outer space
                // this time using the location camera entering method
                const locationInfo = modelObj.locationInfo;
                that.changeCameraLocation({}, true, () => {
                    that.changeCameraLocation({}, true, () => {
                        that.renderModel(modelObj, staticMapLoadFlag, visualLevelOfModelToRender);
                    }, locationInfo.areaLongitude, locationInfo.areaLatitude, (locationInfo.areaHeight / MyCesiumConsts.CUSTOM_CAMERA_PROPORTION_FROM_SPACE));
                }, locationInfo.areaLongitude, locationInfo.areaLatitude, locationInfo.areaHeight);
            } else if (visualLevelOfModelToRender === _VISUAL_LEVEL.INNER_ROOM){
                // using the method of 'REPLACE' to render inner room outer box and inner decoration but not 'ADD'
                // need to do a series process, using the service of loading
                const loading = that.createLoadingInstance(StaticHints.LOADING_TO_INNER_ROOM.replace('{}', modelObj.modelName));
                // 'REPLACE' until the first complex model
                const prePrimitives = viewer.scene.primitives;
                const lastIndexOfPrimitive = prePrimitives.length - 1;
                for (let i = lastIndexOfPrimitive; i > 0; i--) {
                    const tilesetToRemove = prePrimitives.get(i);
                    const removeResult = prePrimitives.remove(tilesetToRemove);
                    if (!removeResult) {
                        console.log('Fail to remove parent tilesets when loading the room and its inner model!');
                    }
                }
                // accessibleFlag = true;
                getInnerRoomModel({
                    parentId: modelObj.myId
                }).then(res => {
                    if (res.data) {
                        that.renderInnerRoom(modelObj, res.data, loading, visualLevelOfModelToRender);
                    }
                });
            } else {
                // that.changeCameraLocation({}, true, () => {
                //     that.renderModel(modelObj, staticMapLoadFlag, visualLevelOfModelToRender);
                // }, locationInfo.areaLongitude, locationInfo.areaLatitude, locationInfo.areaHeight);
                that.renderModel(modelObj, staticMapLoadFlag, visualLevelOfModelToRender, accessibleFlag);
            }    
        },
        renderModel(modelObj, staticMapLoadFlag, visualLevelOfModelToRender, isAccessible) {
            var that = this;
            const hprData = modelObj.entranceInfo.headingPitchRoll;
            const destinationData = modelObj.entranceInfo.destination;
            const infoData = modelObj.cultureInfo;
            const _VISUAL_LEVEL = MyCesiumConsts.VISUAL_LEVEL;
            const viewer = window.viewer.viewerInstance;

            if (modelObj.modelPath && modelObj.modelPath.length !== 0) {
                // load model after area camera moving
                if (!modelObj.translationArr || modelObj.translationArr.length === 0) {
                    modelObj.translationArr = "[0, 0, 0]";
                }
                const modelPromise = TilesetHandler.loadTilesetWithInitClippingBox(modelObj.modelPath, viewer.scene, {
                    translation: JSON.parse(modelObj.translationArr),
                    isAccessible 
                });
                modelPromise.then((tileset) => {
                    console.log(tileset);
                    that.changeCameraLocation(hprData, false, () => {
                        if (visualLevelOfModelToRender === _VISUAL_LEVEL.INNER_FLOOR) {
                            ClippingBoxManager.adjustClippingBoxAroundCamera(tileset, new Cartesian3(
                                destinationData.cartesianX, destinationData.cartesianY, destinationData.cartesianZ
                            ), modelObj.averageFloorHeight);
                        }
                        if (visualLevelOfModelToRender === _VISUAL_LEVEL.FEATURE_FOCUS) {
                            // move the parent model clipping box to the focus one
                            const parentTileset = viewer.scene.primitives.get(that.modelStorage.index);
                            ClippingBoxManager.adjustClippingBox2FeatureFocus(parentTileset, tileset);
                            // update the aside data to floor and room
                            getFloorAndRoomAsideData({
                                parentId: modelObj.myId
                            }).then(res => {
                                const floorAndRoomAside = res.data;
                                that.asideLocationList = floorAndRoomAside;
                                // correct aside visual level
                                that.asideVisualLevel = visualLevelOfModelToRender + 1;
                            });
                            this.issueNotification(StaticHints.FEATURE_FOCUS_ROTATE_MSG, StaticHints.OPERATION_NOTIFICATION_TITLE);
                        } else {
                            // synchronize visual level when visualLevelOfModelToRender is not FEATURE_FOCUS
                            that.asideVisualLevel = visualLevelOfModelToRender;
                        }
                        // issue the notification of operation method
                        if (visualLevelOfModelToRender === _VISUAL_LEVEL.INNER_FLOOR) {
                            that.notifyInnerOperation();
                        }
                        // show or modify title hint when it is the outer model
                        if (staticMapLoadFlag) {
                            that.locationHintTransition();
                            that.staticMapData.modelName = modelObj.modelName;
                            that.$refs.staticMap.changeParentCameraLoadingStatus(false);
                        }

                        // automatically open the info dialog
                        that.showMainInfoDialog(infoData);

                        that.visualLevel = visualLevelOfModelToRender;
                        if (!that.changeCameraMode) {
                            // do after model entrance camera location
                            that.userInputState = true;
                        } else {
                            that.btnShowState = true;
                        }
                        // save part of model info which is focused now
                        this.updateModelStorage(viewer, {
                            modelId: modelObj.myId,
                            destination: destinationData,
                            hpr: hprData
                        });
                        // push model to record stack
                        console.log(that.modelStack.length);
                        that.modelStack.push({
                            modelId: modelObj.myId,
                            tempVisualLevel: that.visualLevel
                        });
                        console.log(that.modelStack.length);
                    }, destinationData.cartesianX, destinationData.cartesianY, destinationData.cartesianZ);
                });
            } else {
                // it must be in the same model, so it is unnecessary to change tileset index, but just other info
                // only need to adjust camera position and hpr
                that.changeCameraLocation(hprData, false, () => {
                    if (infoData && infoData !== null) {
                        that.$refs.subDialog.setInfoProperty(infoData);
                    }
                    // can't update the present entrance when viewing the item in the room
                    if (visualLevelOfModelToRender !== (_VISUAL_LEVEL.INNER_ROOM + 1)) {
                        // render the selected aside
                        that.modelStorage.destination = destinationData;
                        that.modelStorage.hpr = hprData;
                    }
                }, destinationData.cartesianX, destinationData.cartesianY, destinationData.cartesianZ);
                if (visualLevelOfModelToRender === _VISUAL_LEVEL.INNER_FLOOR) {
                        ClippingBoxManager.adjustClippingBoxAroundCamera(
                            viewer.scene.primitives.get(that.modelStorage.index),
                            new Cartesian3(destinationData.cartesianX, destinationData.cartesianY, destinationData.cartesianZ),
                            that.preInnerFloorHeight
                        )
                    }
            }
        },
        renderInnerRoom(modelObj, innerDecorationRes, loading, visualLevelOfModelToRender) {
            // need to continuously render outer box and inner decoration
            var that = this;
            const hprData = modelObj.entranceInfo.headingPitchRoll;
            const destinationData = modelObj.entranceInfo.destination;
            const infoData = modelObj.cultureInfo;

            const viewer = window.viewer.viewerInstance;
            if (modelObj.modelPath && modelObj.modelPath.length !== 0 && 
                innerDecorationRes.modelPath && innerDecorationRes.modelPath.length !== 0) {
                    // outer box model of room don't need to render clipping box but as the function of container
                    const outerBoxPromise = TilesetHandler.loadTileset(modelObj.modelPath, viewer.scene, {});
                    outerBoxPromise.then(outerTileset => {
                        // render the model of inner decoration of a room
                        const innerPromise = TilesetHandler.loadTilesetWithInitClippingBox(innerDecorationRes.modelPath, viewer.scene);
                        innerPromise.then(innerTileset => {
                            that.changeCameraLocation(hprData, false, () => {
                                // update aside information to item bar
                                getInteractiveItemAside({
                                    innerRoomId: innerDecorationRes.myId
                                }).then(res => {
                                    if (res.data) {
                                        that.asideLocationList = res.data;
                                    }
                                });
                                // render item highlight
                                getHighlightItemBatchIds({
                                    innerRoomId: innerDecorationRes.myId
                                }).then(res => {
                                    if (res.data) {
                                        TilesetHandler.renderSelectedFeature(innerTileset, res.data.tileMap);
                                    }
                                });
                                // automatically open the info dialog
                                that.showMainInfoDialog(infoData);
                                // correct the aside visual level
                                that.visualLevel = visualLevelOfModelToRender;
                                // store model and its entrance
                                this.updateModelStorage(viewer, {
                                    modelId: innerDecorationRes.myId,
                                    destination: destinationData,
                                    hpr: hprData
                                });
                                that.modelStack.push({
                                    modelId: modelObj.myId,
                                    tempVisualLevel: that.visualLevel
                                }, {
                                    modelId: innerDecorationRes.myId,
                                    tempVisualLevel: that.visualLevel
                                });
                                that.asideVisualLevel = visualLevelOfModelToRender + 1;
                                //TODO: adjust model clipping box to camera
                                // ClippingBoxManager.adjustClippingBoxAroundCamera(innerTileset, new Cartesian3(destinationData.cartesianX, destinationData.cartesianY, destinationData.cartesianZ));
                                loading.close();
                                that.locationHintTransition();
                                this.btnShowState = true;
                                that.notifyInnerOperation();
                                that.issueNotification(StaticHints.INTERACTIVE_ITEM_MSG, StaticHints.OPERATION_NOTIFICATION_TITLE);
                            }, destinationData.cartesianX, destinationData.cartesianY, destinationData.cartesianZ);
                        });
                    });
            }
        },
        setExtraConfigWhenInit(viewer) {
            const scene = viewer.scene;
            // resolve the problem that some underground feature will move with camera
            scene.globe.depthTestAgainstTerrain = true;
            // scene.debugShowFramesPerSecond = true;
        },
        loadGlobeSurface(viewer) {
            // default baseLayer in viewer using ImageryLayer.fromWorldImagery() to get satellite imagery
            // Bing map has coordination problem with satellite imagery and road imagery(dislocation)
            // so using Amap satellite imagery and Bing road imagery
            // remove cesium default imagery layers and providers
            viewer.imageryLayers.removeAll();
            // load Amap satellite imagery at bottom
            const aerialLayer = new UrlTemplateImageryProvider({
                url: MyAmapConsts.SATELLITE_IMAGERY,
                minimumLevel: 3,
                maximumLevel: 18
            });
            viewer.imageryLayers.addImageryProvider(aerialLayer);
            // const test = ImageryLayer.fromWorldImagery({
            //     style: IonWorldImageryStyle.AERIAL
            // });
            // viewer.imageryLayers.add(test);
            // load Bing road imagery at top
            const bingMapLayer = ImageryLayer.fromWorldImagery({
                style: IonWorldImageryStyle.ROAD
            });
            bingMapLayer.alpha = 0.4;
            viewer.imageryLayers.add(bingMapLayer);
            // wait to load imagery
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(true);
                }, 1500);
            });
        },
        locationHintTransition() {
            var that = this;
            that.locationHint.show = true;
            setTimeout(() => {
                that.locationHint.show = false;
            }, 3000);
        },
        changeCameraLocation(orientation = {}, isDegree = true, completeCallback = () => {}, ...destination) {
            if (window.viewer && window.viewer.viewerInstance) {
                const viewer = window.viewer.viewerInstance;
                const camera = viewer.camera;
                var _destination;
                if (isDegree) {
                    // degree => cartesian3
                    _destination = Cartesian3.fromDegrees(...destination);
                } else {
                    // radians => cartesian3
                    _destination = Cartesian3.fromArray(destination);
                }
                camera.flyTo({
                    duration: 4,
                    destination: _destination,
                    orientation,
                    complete: completeCallback,
                });
            }
        },
        handleAsideButtonClick(event) {
            this.$refs.aside.openParentDrawer(event);
        },
        handleBackButtonClick(event) {
            var that = this;
            that.$confirm(StaticHints.BACK, StaticHints.BACK_HINT, {
                confirmButtonText: StaticHints.CONFIRM,
                cancelButtonText: StaticHints.CANCEL,
                type: 'warning'
            }).then(() => {
                const viewer = window.viewer.viewerInstance;
                const primitives = viewer.scene.primitives;
                const _VISUAL_LEVEL = MyCesiumConsts.VISUAL_LEVEL;
                const lastModel = that.modelStack[that.modelStack.length - 2];
                if (!lastModel) {
                    that.issueNotification(StaticHints.CANNOT_BACK_MSG, StaticHints.ERROR_NOTIFICATION_TITLE, 'error');
                }
                const visualLevelOfModelToRender = lastModel.tempVisualLevel;
                // dispatch handle method by visual level
                switch(visualLevelOfModelToRender) {
                    case _VISUAL_LEVEL.GLOBE_SURFACE:
                        // update aside
                        getModelAsideData().then(res => {
                            if (res && res.data && res.data.length !== 0) {
                                that.asideLocationList = res.data;
                            }
                        });
                        getRestoreFeature({
                            childId: that.modelStorage.modelId
                        }).then(res => {
                            if (res.data) {
                                const result = res.data;
                                const replacedFeatures = result.replacedFeatures;
                                const hprData = result.entranceInfo.headingPitchRoll;
                                const destinationData = result.entranceInfo.destination;
                                that.userInputState = false;
                                that.updateMiniMap(result.entranceInfo, visualLevelOfModelToRender);
                                ClippingBoxManager.adjustClippingBox2SurfaceLevel(primitives.get(that.modelStorage.index - 1));
                                // only need to change camera position as parent model already exist
                                that.changeCameraLocation(hprData, false, () => {
                                    primitives.remove(primitives.get(that.modelStorage.index));
                                    replacedFeatures.forEach(replacedFeature => {
                                        TilesetHandler.restoreFeaturesAfterLoad(
                                            primitives.get(that.modelStorage.index - 1),
                                            replacedFeature.tileName,
                                            replacedFeature.batchIds
                                        )
                                    });

                                    // show location hint as entering
                                    that.locationHint.title = result.modelName;
                                    that.locationHintTransition();
                                    that.$refs.staticMap.changeParentCameraLoadingStatus(false);

                                    // automatically show info dialog
                                    that.showMainInfoDialog(result.cultureInfo);
                                    that.userInputState = true;
                                    that.visualLevel = visualLevelOfModelToRender;
                                    that.asideVisualLevel = that.asideVisualLevel - 2;

                                    // remove the last present model and back to the last one
                                    that.modelStack.pop();
                                    this.updateModelStorage(viewer, {
                                        modelId: result.modelId,
                                        destination: destinationData,
                                        hpr: hprData
                                    });
                                }, destinationData.cartesianX, destinationData.cartesianY, destinationData.cartesianZ);
                            }
                        });
                        break;
                    case _VISUAL_LEVEL.FEATURE_FOCUS:
                        getSingleModelAsAside({
                            myId: lastModel.modelId
                        }).then(res => {
                            const result = res.data;
                            const destinationData = result.entranceInfo.destination;
                            const hprData = result.entranceInfo.headingPitchRoll;
                            that.userInputState = false;
                            that.changeCameraLocation(hprData, false, () => {
                                primitives.remove(primitives.get(that.modelStorage.index));
                                that.locationHint.title = result.modelName;
                                that.locationHintTransition();
                                that.userInputState = true;
                                that.visualLevel = visualLevelOfModelToRender;
                                that.modelStack.pop();
                                this.issueNotification(StaticHints.FEATURE_FOCUS_ROTATE_MSG, StaticHints.OPERATION_NOTIFICATION_TITLE);
                                that.showMainInfoDialog(result.cultureInfo);
                                this.updateModelStorage(viewer, {
                                    modelId: result.myId,
                                    destination: destinationData,
                                    hpr: hprData
                                });
                            }, destinationData.cartesianX, destinationData.cartesianY, destinationData.cartesianZ);
                        });
                        break;    
                    case _VISUAL_LEVEL.INNER_ROOM:
                        // const loading = that.createLoadingInstance(StaticHints.DEFAULT_BACK_LOADING);
                        var isInnerFloor = false;
                        // continue to get the third to the last model
                        const thirdToLast = that.modelStack[that.modelStack.length - 3];
                        var lastModelId = thirdToLast.modelId;
                        // judge whether user has jumped the process of INNER_FLOOR
                        if (thirdToLast.tempVisualLevel === _VISUAL_LEVEL.INNER_FLOOR) {
                            // if the last is INNER_FLOOR, correct model id in model stack
                            const fourthToLast = that.modelStack[that.modelStack.length - 4];
                            lastModelId = fourthToLast.modelId;
                            isInnerFloor = true;
                        }

                        primitives.remove(primitives.get(primitives.length - 1));
                        primitives.remove(primitives.get(primitives.length - 1));
                        that.modelStack.pop();
                        that.modelStack.pop();


                        // whatever the last model is INNER_FLOOR or FEATURE_FOCUS, they all need to get feature focus model
                        getSingleModelAsAside({
                            myId: lastModelId
                        }).then(focusRes => {
                            if (isInnerFloor) {
                                getFloorWithEntrance({
                                    parentId: lastModelId,
                                    roomId: lastModel.modelId
                                }).then(innerRes => {
                                    const result = innerRes.data;
                                    const floor = result.floorBO;
                                    // restore the FEATURE_FOCUS one because the render process below won't push in stack again
                                    that.modelStack.pop();
                                    that.renderModel(focusRes.data, false, thirdToLast.tempVisualLevel - 1, false);
                                    that.renderModel({
                                        myId: floor.myId,
                                        modelPath: floor.modelPath,
                                        averageFloorHeight: floor.averageFloorHeight,
                                        translationArr: floor.translationArr,
                                        entranceInfo: result.entranceInfo
                                    }, false, thirdToLast.tempVisualLevel, false);
                                    getFloorAndRoomAsideData({
                                        parentId: lastModelId
                                    }).then(res => {
                                        that.asideLocationList = res.data;
                                        that.$refs.mainDialog.setInfoProperty(focusRes.data.cultureInfo, false);
                                    });
                                });
                            } else {
                                that.modelStack.pop();
                                that.renderLocation(focusRes.data, thirdToLast.tempVisualLevel);
                            }

                        });
                        break;
                    default:
                        that.issueNotification(StaticHints.CANNOT_BACK_MSG, StaticHints.ERROR_NOTIFICATION_TITLE, 'error');
                }
                that.cancelButtonFocus(event);
            }).catch(() => {
                that.cancelButtonFocus(event);
            });
        },
        handleBack2EntranceClick(event) {
            var that = this;
            that.$confirm(StaticHints.BACK_TO_ENTRANCE, StaticHints.BACK_HINT, {
                confirmButtonText: StaticHints.CONFIRM,
                cancelButtonText: StaticHints.CANCEL,
                type: 'warning'
            }).then(() => {
                //TODO: judge whether clipping box need to move
                that.userInputState = false;
                const _model = that.modelStorage;
                const _destination = _model.destination;
                that.changeCameraLocation(_model.hpr, false, () => {
                    if (that.visualLevel > MyCesiumConsts.VISUAL_LEVEL.FEATURE_FOCUS) {
                        that.btnShowState = true;
                    } else {
                        that.userInputState = true;
                    }
                }, _destination.cartesianX, _destination.cartesianY, _destination.cartesianZ);
                that.cancelButtonFocus(event);
            }).catch(() => {
                that.cancelButtonFocus(event);
            });
        },
        handleClickRotate(event) {
            var that = this;
            that.isShowClickRotate = !that.isShowClickRotate;
            that.cancelButtonFocus(event);
        },
        handleInfoButtonClick(event) {
            var that = this;
            that.$refs.mainDialog.openDialog();
            that.cancelButtonFocus(event);
        },
        handleFeatureClick(infoData) {
            this.$refs.subDialog.setInfoProperty(infoData);
        },
        cancelButtonFocus(event) {
            // handle the problem of button focus
            var targetElement = event.target;
            // click on the icon element
            if (targetElement.nodeName === 'I') {
                targetElement = targetElement.parentNode;
            }
            targetElement.blur();
        },
        issueNotification(message = '', title = StaticHints.DEFAULT_NOTIFICATION_TITLE, type = 'success') {
            const h = this.$createElement;
            this.$notify({
                title,
                message: h('div', {style: {color: 'black', fontWeight: 'bold', fontSize: '1.5vh'}}, message),
                position: 'top-left',
                offset: 100,
                showClose: false,
                type,
                duration: 3500,
            });
        },
        notifyInnerOperation() {
            setTimeout(() => {
                this.issueNotification(StaticHints.INNER_ROTATE_MSG, StaticHints.OPERATION_NOTIFICATION_TITLE);
            }, 1000);
            setTimeout(() => {
                this.issueNotification(StaticHints.INNER_MOVE_MSG, StaticHints.OPERATION_NOTIFICATION_TITLE);
            }, 2000);
        },
        showMainInfoDialog(infoData) {
            if (infoData && infoData !== null) {
                setTimeout(() => {
                    this.$refs.mainDialog.setInfoProperty(infoData);
                }, 2000);
            }
        },
        updateMiniMap(targetCartesian3, visualLevelOfModelToRender) {
            var that = this;
            const parseResult = that.parseCartesianEntrance2Cartographic(targetCartesian3);
            console.log(parseResult);
            // need to change static map when camera is outer
            const _staticMap = that.$refs.staticMap;
            // clear image rendered now and then get new one
            if (visualLevelOfModelToRender === MyCesiumConsts.VISUAL_LEVEL.FEATURE_FOCUS) {
                that.staticMapData.zoomLevel = 16;
            } else {
                that.staticMapData.zoomLevel = 12;
            }
            that.$nextTick(() => {
                _staticMap.clearUrl();
                _staticMap.loadAmapStaticMap(Math.toDegrees(parseResult.longitude), Math.toDegrees(parseResult.latitude));
                _staticMap.changeParentCameraLoadingStatus(true);
            });
        },
        parseCartesianEntrance2Cartographic(entranceInfo) {
            const destination = entranceInfo.destination;
            return Cartographic.fromCartesian(new Cartesian3(
                destination.cartesianX, destination.cartesianY, destination.cartesianZ
            ));
        },
        updateModelStorage(viewer, newModelStorage) {
            var that = this;
            that.modelStorage.modelId = newModelStorage.modelId;
            that.modelStorage.destination = newModelStorage.destination;
            that.modelStorage.hpr = newModelStorage.hpr;
            that.modelStorage.index = viewer.scene.primitives.length - 1;
        },
        createLoadingInstance(msg) {
            return this.$loading({
                    lock: true,
                    text: msg,
                    background: 'rgba(0, 0, 0, 1)'
                });
        }
    }
}
</script>

<style lang="scss" scoped>
    .cesium-container {
        position: relative;
        width: 100%;
        height: 100%;

        .location-hint-container {
            width: fit-content;
            position: absolute;
            left: 0;
            right: 0;
            margin: auto;
            text-align: center;
            padding: 18px 150px 5.8px 150px;
            border-bottom: 3px solid;
            border-image: linear-gradient(90deg, rgba(54, 57, 56, .1) 0%, rgba(76, 80, 81, .8),rgba(54, 57, 56, .1) 100%) 2 2 2 2;
            z-index: 1;

            &__title {
                color: rgba(255, 255, 255, 1);
                font-size: 5.5vh;
                font-family: '宋体', sans-serif;
                text-shadow: rgb(0, 0, 0) 0 0 5px;
            }
        }

        .static-map {
            position: absolute;
            left: 12px;
            bottom: 12px;
            z-index: 1;
        }

        .function-area-container {
            position: absolute;
            left: 0;
            right: 0;
            top: 0;
            background: transparent;
            margin: auto;
            z-index: 1;
            $button-margin: 15px 8px 10px 8px;
            $button-icon-size: 28px;

            .aside-function {
                float: right;
                margin: 15px 20px 10px 8px;
                font-size: $button-icon-size;
            }

            .show-info-dialog-function {
                float: left;
                margin: $button-margin;
                font-size: $button-icon-size;
            }

            .back-function {
                float: left;
                margin: 15px 8px 10px 20px;
                font-size: $button-icon-size;
            }

            .back-to-entrance-function {
                float: left;
                margin: $button-margin;
                font-size: $button-icon-size;
            }

            .show-click-rotate {
                float: left;
                margin: $button-margin;
                font-size: $button-icon-size;
            }

        }

        .feature-focus-container {
            position: absolute;
        }

    }
    
    .opacity-change-enter-active {
        transition: opacity 3s ease;
    }

    .opacity-change-leave-active {
        transition: opacity 1s ease;
    }

    .opacity-change-enter,
    .opacity-change-leave-to {
        opacity: 0;
    }
</style>